﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using LaughingDog.Input;

namespace LaughingDog.Managers
{    
    public sealed class InputManager : GameComponent
    {
        #region Private Members

        private Mouse mMouse = new Mouse();
        private Keyboard mKeyboard = new Keyboard();
        private List<GamePad> mGamePads = new List<GamePad>();
        private List<InputMapping> mInputMappings = new List<InputMapping>();

        #endregion

        #region Public Properties

        /// <summary>
        /// Gets the Mouse object that is being used.
        /// </summary>
        public Mouse Mouse { get { return mMouse; } }

        /// <summary>
        /// Gets the Keyboard object that is being used.
        /// </summary>
        public Keyboard Keyboard { get { return mKeyboard; } }

        /// <summary>
        /// Gets a list of all connected XBox360GamePads object that is being used.
        /// </summary>
        public List<GamePad> GamePads { get { return mGamePads; } }

        #endregion

        #region Singleton

        static readonly InputManager instance = new InputManager(Engine.Game);

        // Explicit static constructor to tell C# compiler
        // not to mark type as beforefieldinit
        static InputManager()
        {
        }

        InputManager(Game game) 
            : base (game) 
        {
        }

        public static InputManager Instance
        {
            get
            {
                return instance;
            }
        }

        #endregion

        #region Public Methods

        /// <summary>
        /// Updates the InputManager.
        /// </summary>
        /// <param name="gameTime">A snapshot of the current game time.</param>
        public override void Update(GameTime gameTime)
        {
            Mouse.Update();
            Keyboard.Update();

            base.Update(gameTime);
        }
       
        /// <summary>
        /// Adds a global InputMapping to the manager.
        /// </summary>
        /// <param name="mapping">The InputMapping to add.</param>
        /// <returns>The mapping that was added.</returns>
        public InputMapping AddInputMapping(InputMapping mapping)
        {
            mInputMappings.Add(mapping);

            return mapping;
        }

        /// <summary>
        /// Adds a player specific InputMapping to the manager.
        /// </summary>
        /// <param name="mapping">The InputMapping to add.</param>
        /// <param name="playerIndex">The PlayerIndex number to associate with the mapping.</param>
        /// <returns>The mapping that was added.</returns>
        public InputMapping AddInputMapping(InputMapping mapping, PlayerIndex playerIndex)
        {
            mapping.PlayerIndex = playerIndex;

            mInputMappings.Add(mapping);

            return mapping;
        }

        /// <summary>
        /// Creates and adds a global InputMapping to the manager.
        /// </summary>
        /// <param name="mappingName">The name of the InputMapping to create and add.</param>
        /// <returns>The mapping that was added.</returns>
        public InputMapping AddInputMapping(String mappingName)
        {
            InputMapping mapping = new InputMapping(mappingName);

            mInputMappings.Add(mapping);

            return mapping;
        }

        /// <summary>
        /// Creates and adds a player specific InputMapping to the manager.
        /// </summary>
        /// <param name="mappingName">The name of the InputMapping to create and add.</param>
        /// <param name="playerIndex">The PlayerIndex number to associate with the mapping.</param>
        /// <returns>The mapping that was added.</returns>
        public InputMapping AddInputMapping(String mappingName, PlayerIndex playerIndex)
        {
            InputMapping mapping = new InputMapping(mappingName);

            mapping.PlayerIndex = playerIndex;

            mInputMappings.Add(mapping);

            return mapping;
        }

        /// <summary>
        /// Removes an InputMapping from the manager.
        /// </summary>
        /// <param name="mappingName">The name of the InputMapping to remove.</param>
        public void RemoveInputMapping(String mappingName)
        {
            foreach (InputMapping mapping in new List<InputMapping>(mInputMappings))
                if(mapping.Name == mappingName)
                    mInputMappings.Remove(mapping);            
        }

        /// <summary>
        /// Retrieves an InputMapping from the manager.
        /// </summary>
        /// <param name="mappingName">The name of the InputMapping to retrieve.</param>
        /// <returns>The retrieved mapping.</returns>
        public InputMapping GetInputMapping(String mappingName)
        {            
            foreach (InputMapping mapping in new List<InputMapping>(mInputMappings))
                if (mapping.Name == mappingName)
                    return mapping;            

            return null;
        }

        /// <summary>
        /// Retrieves an InputMapping from the manager.
        /// </summary>
        /// <param name="mappingName">The name of the InputMapping to retrieve.</param>
        /// <param name="playerIndex">The player index associated with the InputMapping to retrieve.</param>
        /// <returns>The retrieved mapping.</returns>
        public InputMapping GetInputMapping(String mappingName, PlayerIndex playerIndex)
        {
            foreach (InputMapping mapping in new List<InputMapping>(mInputMappings))
                if (mapping.Name == mappingName)
                    if(mapping.PlayerIndex == playerIndex)
                        return mapping;

            return null;
        }

        /// <summary>
        /// Checks whether any inputs associated with this mapping are currently pressed.
        /// </summary>
        /// <param name="mappingName">The name of of the mapping to check.</param>
        /// <returns></returns>
        public Boolean InputPressed(String mappingName)
        {
            InputMapping foundMapping = GetInputMapping(mappingName);

            if (foundMapping != null)
                return foundMapping.InputPressed();            

            return false;
        }

        /// <summary>
        /// Checks whether any inputs associated with this mapping are currently pressed.
        /// </summary>
        /// <param name="mappingName">The name of of the mapping to check.</param>
        /// <param name="playerIndex">The player index to check on.</param>
        /// <returns></returns>
        public Boolean InputPressed(String mappingName, PlayerIndex playerIndex)
        {
            InputMapping foundMapping = GetInputMapping(mappingName, playerIndex);            

            if (foundMapping != null)
                return foundMapping.InputPressed();

            return false;
        }

        /// <summary>
        /// Checks whether any inputs associated with this mapping were just pressed.
        /// </summary>
        /// <param name="mappingName">The name of of the mapping to check.</param>
        /// <returns>Returns true if the input was just pressed, false if not.</returns>
        public Boolean InputJustPressed(String mappingName)
        {
            InputMapping foundMapping = GetInputMapping(mappingName);           

            if (foundMapping != null)
                return foundMapping.InputJustPressed();    

            return false;
        }

        /// <summary>
        /// Checks whether any inputs associated with this mapping were just pressed.
        /// </summary>
        /// <param name="mappingName">The name of of the mapping to check.</param>
        /// <param name="playerIndex">The player index to check on.</param>
        /// <returns>Returns true if the input was just pressed, false if not.</returns>
        public Boolean InputJustPressed(String mappingName, PlayerIndex playerIndex)
        {
            InputMapping foundMapping = GetInputMapping(mappingName, playerIndex);          

            if (foundMapping != null)
                return foundMapping.InputJustPressed();

            return false;
        }

        /// <summary>
        /// Checks whether any inputs associated with this mapping were just pressed.
        /// </summary>
        /// <param name="mappingName">The name of of the mapping to check.</param>
        /// <returns>Returns true if the input was just released, false if not.</returns>
        public Boolean InputJustReleased(String mappingName)
        {
            InputMapping foundMapping = GetInputMapping(mappingName);

            if (foundMapping != null)
                return foundMapping.InputJustReleased();    

            return false;
        }

        /// <summary>
        /// Checks whether any inputs associated with this mapping were just pressed.
        /// </summary>
        /// <param name="mappingName">The name of of the mapping to check.</param>
        /// <param name="playerIndex">The player index to check on.</param>
        /// <returns>Returns true if the input was just released, false if not.</returns>
        public Boolean InputJustReleased(String mappingName, PlayerIndex playerIndex)
        {
            InputMapping foundMapping = GetInputMapping(mappingName, playerIndex);

            if (foundMapping != null)
                return foundMapping.InputJustReleased();

            return false;
        }

        /// <summary>
        /// Checks whether any inputs associated with this mapping are released.
        /// </summary>
        /// <param name="mappingName">The name of of the mapping to check.</param>
        /// <returns>Returns true if the input is released, false if not.</returns>
        public Boolean InputReleased(String mappingName)
        {
            InputMapping foundMapping = GetInputMapping(mappingName);

            if (foundMapping != null)
                return foundMapping.InputReleased();    

            return false;
        }

        /// <summary>
        /// Checks whether any inputs associated with this mapping are released.
        /// </summary>
        /// <param name="mappingName">The name of of the mapping to check.</param>
        /// <param name="playerIndex">The player index to check on.</param>
        /// <returns>Returns true if the input is released, false if not.</returns>
        public Boolean InputReleased(String mappingName, PlayerIndex playerIndex)
        {
            InputMapping foundMapping = GetInputMapping(mappingName, playerIndex);

            if (foundMapping != null)
                return foundMapping.InputReleased();

            return false;
        }

        #endregion
    }
}
